<script setup>
import { onMounted } from 'vue'

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { Line2 } from 'three/examples/jsm/lines/Line2.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';

onMounted(() => {
  init();
})

function init() {
  let geturl = window.location.href
  let getqyinfo = geturl.split('?')[1]
  let getqys = new URLSearchParams('?' + getqyinfo)
  let id = getqys.get('id')

  const modelColor = "#8AC";
  const modelNum = 2;
  const models = [];
  for (let i = 1; i <= modelNum; i++) {
    models.push(i.toString());
  }

  const c = document.querySelector('#canvas');
  const w = c.clientWidth;
  const h = c.clientHeight;
  const control = document.querySelector('#control')
  const renderer = new THREE.WebGLRenderer({ canvas: c, antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(w, h, false);
  renderer.autoClear = false;

  //创建透视和正交相机，以及对应的控制器
  const pCamera = new THREE.PerspectiveCamera(45, w / h, 0.1, 1000);
  pCamera.position.set(-20, 20, 20);
  const oCamera = new THREE.OrthographicCamera(-w / 70, w / 70, h / 70, -h / 70, 0.001, 1000);
  oCamera.position.set(-20, 20, 20);
  const pControls = new OrbitControls(pCamera, control);
  pControls.target.set(0, 0, 0);
  pControls.update();
  const oControls = new OrbitControls(oCamera, control);
  oControls.target.set(0, 0, 0);
  oControls.update();

  //按渲染顺序编号的场景，分别包含三视图、模型、线框
  const scene1 = new THREE.Scene();
  scene1.background = new THREE.Color(0x808080);
  const scene2 = new THREE.Scene();
  const ambientLight = new THREE.AmbientLight(0x808080);
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
  directionalLight.position.set(-1, 1, 0.5)
  const pointLight = new THREE.PointLight(0xffffff, 2, 100);
  pointLight.position.set(20, 20, 20);
  scene2.add(ambientLight, pointLight, directionalLight);
  const scene3 = new THREE.Scene();

  var currentModel = null;
  var anotherModel = null;
  var lineBuffer = null;//不透明stl模型的线框（如果有）
  var isTransparent = false;
  const transparentMaterial = new THREE.MeshBasicMaterial({ color: modelColor, transparent: true, opacity: 0.6 });
  const opaqueMaterial = new THREE.MeshStandardMaterial({ color: modelColor });

  async function addModel(name) {
    //三视图
    const t1 = new THREE.TextureLoader().load('resources/' + name + '-2.png');//左视图
    const m1 = new THREE.MeshBasicMaterial({ transparent: true, map: t1, side: THREE.DoubleSide });
    const m2 = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });
    const m3 = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });
    const t4 = new THREE.TextureLoader().load('resources/' + name + '-3.png');//俯视图
    const m4 = new THREE.MeshBasicMaterial({ transparent: true, map: t4, side: THREE.DoubleSide });
    const m5 = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });
    const t6 = new THREE.TextureLoader().load('resources/' + name + '-1.png');//主视图
    const m6 = new THREE.MeshBasicMaterial({ transparent: true, map: t6, side: THREE.DoubleSide });
    const materials = [m1, m2, m3, m4, m5, m6];
    const geometry = new THREE.BoxGeometry(30, 30, 30);
    const views = new THREE.Mesh(geometry, materials);
    scene1.add(views);


    var loadedGLTF = false;//是否已加载glhf模型
    const gltfLoader = new GLTFLoader();
    await gltfLoader.loadAsync('resources/' + name + '.glb').then((model) => {
      const gltfModelScale = 50;
      model.scene.scale.set(gltfModelScale, gltfModelScale, gltfModelScale);
      if (isTransparent) {
        anotherModel = model.scene;
      } else {
        scene2.add(model.scene);
        currentModel = model.scene;
      };
      loadedGLTF = true;
    }).catch((e) => { });

    const stlLoader = new STLLoader();
    stlLoader.load('resources/' + name + '.STL', function (geometry) {
      const stlModelScale = 0.0375;
      geometry.scale(stlModelScale, stlModelScale, stlModelScale);
      geometry.translate(-5, -5, -5);
      geometry.center();
      const transparentModel = new THREE.Mesh(geometry, transparentMaterial);
      if (isTransparent) {
        scene2.add(transparentModel);
        currentModel = transparentModel;
      } else {
        anotherModel = transparentModel;
      }
      if (!loadedGLTF) {
        const edgess = new THREE.EdgesGeometry(geometry);
        lineBuffer = new THREE.LineSegments(edgess, new THREE.LineBasicMaterial({ color: 0x00 }));//线框
        const opaqueModel = new THREE.Mesh(geometry, opaqueMaterial);
        if (isTransparent) {
          anotherModel = opaqueModel;
        } else {
          scene2.add(opaqueModel, lineBuffer);
          currentModel = opaqueModel;
        }
      }

      const edges = new THREE.EdgesGeometry(geometry, 20);
      const showEdgePoints = edges.attributes.position.array;
      if (showEdgePoints.length != 0) {
        const lineMaterial = new LineMaterial({ color: 0x000000, linewidth: 0.003 });
        for (let i = 0; i < showEdgePoints.length; i += 6) {
          const pointPair = showEdgePoints.slice(i, i + 6);
          const lineGeometry = new LineGeometry();
          lineGeometry.setPositions(pointPair);
          const line = new Line2(lineGeometry, lineMaterial);
          scene3.add(line);
        }
      }
      edges.dispose();
    });
  }

  function delModel() {
    const views = scene1.children[0];
    scene1.remove(views);
    views.geometry.dispose();
    views.material.forEach(element => {
      if (element.map != null) {
        element.map.dispose();
      }
      element.dispose();
    });

    if (lineBuffer != null) {
      scene2.remove(lineBuffer);
      lineBuffer.geometry.dispose();
      lineBuffer.material.dispose();
      lineBuffer = null;
    }

    scene2.remove(currentModel);
    if (currentModel.type == 'Mesh') {
      currentModel.geometry.dispose();
    } else {
      anotherModel.geometry.dispose();
    }
    currentModel = null;
    anotherModel = null;

    if (scene3.children.length != 0) {
      const lineMaterial = scene3.children[0].material;
      for (let i = scene3.children.length - 1; i >= 0; i--) {
        const line = scene3.children[i];
        scene3.remove(scene3.children[i]);
        line.geometry.dispose();
      }
      lineMaterial.dispose();
    }

  }

  //窗口使用透视还是正交相机
  var cameraType = 'p';
  const cameraTypes = [['透视', 'p'], ['正交', 'o']];

  //按钮
  {
    const viewSides = [['主视图', 0, 0, 20], ['左视图', -20, 0, 0], ['俯视图', 0, 20, 0]];
    for (const side of viewSides) {
      const button = document.getElementById(side[0]);
      button.onclick = function () {
        oControls.reset();
        pCamera.position.set(side[1], side[2], side[3]);
        oCamera.position.set(side[1], side[2], side[3]);
        pCamera.lookAt(0, 0, 0);
        oCamera.lookAt(0, 0, 0);
        pCamera.updateProjectionMatrix();
        oCamera.updateProjectionMatrix();
        cameraType = 'o';
      };
    }

    for (const type of cameraTypes) {
      const button = document.getElementById(type[0]);
      button.onclick = function () {
        cameraType = type[1];
      };
    }

    const modelSelect = document.getElementById('模型选择');
    for (const name of models) {
      modelSelect.appendChild(new Option(name, name));
    }
    if (id != null) {
      addModel(models[Number(id - 1)]);
      modelSelect.value = id;
    } else {
      addModel(models[0]);
      modelSelect.value = '1';
    }
    modelSelect.onchange = function () {
      delModel();
      addModel(modelSelect.value);
    }

    const swichMaterial = document.getElementById('切换材质');
    swichMaterial.onclick = function () {
      if (isTransparent && lineBuffer != null) {
        scene2.add(lineBuffer);
      } else {
        scene2.remove(lineBuffer);
      }
      scene2.remove(currentModel);
      scene2.add(anotherModel);
      const model = currentModel;
      currentModel = anotherModel;
      anotherModel = model;
      isTransparent = !isTransparent;
    }

    document.addEventListener("keydown", function (event) {
      const i = Number(modelSelect.value);
      if (event.key == "[" && i > 1) {
        delModel();
        addModel((i - 1).toString());
        modelSelect.value = (i - 1).toString();
      }
      else if (event.key == "]" && i < modelNum) {
        delModel();
        addModel((i + 1).toString());
        modelSelect.value = (i + 1).toString();
      }
    });
  }

  //使渲染分辨率匹配显示分辨率，同时调整摄像机纵横比
  function resizeCanvasToDisplaySize() {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    renderer.setSize(width, height, false);
    pCamera.aspect = width / height;
    pCamera.updateProjectionMatrix();
    const h = height / 70;
    const w = width / 70;
    oCamera.left = -w;
    oCamera.right = w;
    oCamera.top = h;
    oCamera.bottom = -h;
    oCamera.updateProjectionMatrix();
  }
  window.addEventListener('resize', resizeCanvasToDisplaySize)

  function render() {
    requestAnimationFrame(render);

    if (cameraType == 'p') {
      renderer.clear();
      renderer.render(scene1, pCamera);
      renderer.clearDepth();
      renderer.render(scene2, pCamera);
      renderer.clearDepth();
      if (isTransparent)
        renderer.render(scene3, pCamera);
    } else {
      renderer.clear();
      renderer.render(scene1, oCamera);
      renderer.clearDepth();
      renderer.render(scene2, oCamera);
      renderer.clearDepth();
      if (isTransparent)
        renderer.render(scene3, oCamera);
    }
  }

  render();
}
</script>


<template>
  <div id="button_div">
    <button id="主视图" type="button">主视图</button>
    <button id="俯视图" type="button">俯视图</button>
    <button id="左视图" type="button">左视图</button>
    <button id="透视" type="button">透视</button>
    <button id="正交" type="button">正交</button>
    <button id="切换材质" type="button">切换材质</button>
    <select id="模型选择" style="float: right;"></select>
  </div>
  <div id="display">
    <canvas id="canvas"></canvas>
    <div id="control"></div>
  </div>
</template>
